Code:
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#define WIN_32_LEAN_AND_MEAN
#include <windows.h>
#include <strsafe.h>
#include <SDL/SDL.h>
#define MAX_RIGHT 79
#define MAX_DOWN 24
#define MAX_THREADS 7
/* Key-input macro and usage credit goes to : http://cboard.cprogramming.com/c-programming/157472-c-pong-program-need-help-smoothing-animations.html */
#define VKEY_IS_PRESSED(vk) ( GetAsyncKeyState(vk) & 0x8000 )
enum v_key
{
up_arrow = VK_UP,
down_arrow = VK_DOWN,
right_arrow = VK_RIGHT,
left_arrow = VK_LEFT,
clearscreen = VK_BACK,
escape = VK_ESCAPE
};
typedef struct shared_game_data
{
SDL_Surface * screen;
SDL_Surface * bmp;
SDL_Rect dstrect;
} shared_game_data;
void * data = NULL;
HANDLE h_threads[MAX_THREADS] = {0};
HANDLE up_arrow_event = 0;
HANDLE down_arrow_event = 0;
HANDLE right_arrow_event = 0;
HANDLE left_arrow_event = 0;
HANDLE clear_screen_event = 0;
HANDLE escape_event = 0;
HANDLE debounce_event = 0;
CRITICAL_SECTION critical_section;
BOOL is_debounce = FALSE;
/* Credit to the board : http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1044844545&id=1043284392 */
void gotoxy(int x, int y)
{
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
/* Credit to Microsoft : http://msdn.microsoft.com/en-us/library/windows/desktop/ms682022(v=vs.85).aspx */
void cls( HANDLE hConsole )
{
COORD coordScreen = { 0, 0 };
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
if( !GetConsoleScreenBufferInfo( hConsole, &csbi ))
return;
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
if( !FillConsoleOutputCharacter( hConsole, (TCHAR) ' ', dwConSize,coordScreen, &cCharsWritten ))
return;
if( !GetConsoleScreenBufferInfo( hConsole, &csbi ))
return;
if( !FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten ))
return;
SetConsoleCursorPosition( hConsole, coordScreen );
}
/* Credit to Microsoft : http://msdn.microsoft.com/en-us/library/windows/apps/ms680582(v=vs.85).aspx */
void ErrorExit(LPTSTR lpszFunction)
{
LPVOID lp_msg_buf;
LPVOID lp_display_buf;
DWORD dw = GetLastError();
FormatMessage
(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lp_msg_buf,
0,
NULL
);
lp_display_buf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lp_msg_buf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lp_display_buf, LocalSize(lp_display_buf) / sizeof(TCHAR), TEXT("%s failed with error %d: %s"), lpszFunction, dw, lp_msg_buf);
MessageBox(NULL, (LPCTSTR)lp_display_buf, TEXT("Error"), MB_OK);
LocalFree(lp_msg_buf);
LocalFree(lp_display_buf);
ExitProcess(dw);
}
void SDL_errorexit( const char * error_message, const char * caption )
{
char buffer[BUFSIZ];
StringCbPrintfA(buffer, BUFSIZ, "%s : %s", error_message, SDL_GetError());
MessageBox( NULL, buffer, caption, MB_OK | MB_ICONEXCLAMATION );
exit(1);
}
void close_events( )
{
CloseHandle(up_arrow_event);
CloseHandle(down_arrow_event);
CloseHandle(right_arrow_event);
CloseHandle(left_arrow_event);
CloseHandle(escape_event);
CloseHandle(clear_screen_event);
CloseHandle(debounce_event);
return;
}
void close_threads( )
{
unsigned index;
for (index = 0; index < MAX_THREADS; index++)
CloseHandle(h_threads[index]);
return;
}
unsigned __stdcall debounce_events( void * m_sleep )
{
const DWORD msleep = *(DWORD *)m_sleep;
DWORD return_value = 0;
HANDLE objects[2] = {debounce_event, escape_event};
for ( ; ; )
{
return_value = WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch ( return_value )
{
case WAIT_OBJECT_0 :
is_debounce = TRUE;
Sleep(msleep);
is_debounce = FALSE;
ResetEvent(objects[0]);
break;
case WAIT_OBJECT_0 + 1 :
return 0;
}
}
return 0;
}
unsigned __stdcall event_loop( void * m_sleep )
{
const DWORD msleep = *(DWORD *)m_sleep;
unsigned iteration = 0;
for ( ; ; )
{
fprintf(stderr,"event_loop iteration : %d\n", iteration++);
if (!is_debounce)
{
if ( VKEY_IS_PRESSED(escape) )
{
if ( !SetEvent(escape_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-Escape"));
}
return 0;
}
if ( VKEY_IS_PRESSED(up_arrow) )
{
if ( !SetEvent(up_arrow_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-UpArrow"));
}
if ( !SetEvent(debounce_event) )
{
close_events();
ErrorExit(TEXT("DebounceEvent-UpArrow"));
}
}
if ( VKEY_IS_PRESSED(down_arrow) )
{
if ( !SetEvent(down_arrow_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-DownArrow"));
}
if ( !SetEvent(debounce_event) )
{
close_events();
ErrorExit(TEXT("DebounceEvent-UpArrow"));
}
}
if ( VKEY_IS_PRESSED(right_arrow) )
{
if ( !SetEvent(right_arrow_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-RightArrow"));
}
if ( !SetEvent(debounce_event) )
{
close_events();
ErrorExit(TEXT("DebounceEvent-UpArrow"));
}
}
if ( VKEY_IS_PRESSED(left_arrow) )
{
if ( !SetEvent(left_arrow_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-LeftArrow"));
}
if ( !SetEvent(debounce_event) )
{
close_events();
ErrorExit(TEXT("DebounceEvent-UpArrow"));
}
}
if ( VKEY_IS_PRESSED(clearscreen) )
{
if ( !SetEvent(clear_screen_event) )
{
close_events();
ErrorExit(TEXT("SetEvent-ClearScreen"));
}
}
}
Sleep(msleep);
}
return 1;
}
unsigned __stdcall left_arrow_handler( void * parameter )
{
DWORD return_value = 0;
HANDLE objects[2] = {left_arrow_event, escape_event};
shared_game_data * my_data = (shared_game_data *)parameter;
for ( ; ; )
{
fprintf(stderr, "left_arrow_event initialized\n");
return_value = WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch ( return_value )
{
case WAIT_OBJECT_0 :
EnterCriticalSection(&critical_section);
if ( (my_data->dstrect.x - 1) > -1 )
{
SDL_FillRect(my_data->screen, NULL, SDL_MapRGB(my_data->screen->format, 0, 0, 0));
--my_data->dstrect.x;
SDL_FillRect(my_data->screen, 0, SDL_MapRGB(my_data->screen->format, 0, 0, 0));
SDL_BlitSurface(my_data->bmp, 0, my_data->screen, &my_data->dstrect);
SDL_Flip(my_data->screen);
}
LeaveCriticalSection(&critical_section);
ResetEvent(objects[0]);
break;
case WAIT_OBJECT_0 + 1 :
return 0;
}
}
return 0;
}
unsigned __stdcall right_arrow_handler( void * parameter )
{
DWORD return_value = 0;
HANDLE objects[2] = {right_arrow_event, escape_event};
shared_game_data * my_data = (shared_game_data *)parameter;
for ( ; ; )
{
fprintf(stderr, "right_arrow_event initialized\n");
return_value = WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch ( return_value )
{
case WAIT_OBJECT_0 :
EnterCriticalSection(&critical_section);
if ( (my_data->dstrect.x + 1) > -1 )
{
SDL_FillRect(my_data->screen, NULL, SDL_MapRGB(my_data->screen->format, 0, 0, 0));
++my_data->dstrect.x;
SDL_FillRect(my_data->screen, 0, SDL_MapRGB(my_data->screen->format, 0, 0, 0));
SDL_BlitSurface(my_data->bmp, 0, my_data->screen, &my_data->dstrect);
SDL_Flip(my_data->screen);
}
LeaveCriticalSection(&critical_section);
ResetEvent(objects[0]);
break;
case WAIT_OBJECT_0 + 1 :
return 0;
}
}
return 0;
}
unsigned __stdcall down_arrow_handler( void * parameter )
{
DWORD return_value = 0;
HANDLE objects[2] = {down_arrow_event, escape_event};
shared_game_data * my_data = (shared_game_data *)parameter;
for ( ; ; )
{
fprintf(stderr, "down_arrow_event initialized\n");
return_value = WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch ( return_value )
{
case WAIT_OBJECT_0 :
EnterCriticalSection(&critical_section);
if ( (my_data->dstrect.y + 1) > -1 )
{
SDL_FillRect(my_data->screen, NULL, SDL_MapRGB(my_data->screen->format, 0, 0, 0));
++my_data->dstrect.y;
SDL_FillRect(my_data->screen, 0, SDL_MapRGB(my_data->screen->format, 0, 0, 0));
SDL_BlitSurface(my_data->bmp, 0, my_data->screen, &my_data->dstrect);
SDL_Flip(my_data->screen);
}
LeaveCriticalSection(&critical_section);
ResetEvent(objects[0]);
break;
case WAIT_OBJECT_0 + 1 :
return 0;
}
}
return 0;
}
unsigned __stdcall up_arrow_handler( void * parameter )
{
DWORD return_value = 0;
HANDLE objects[2] = {up_arrow_event, escape_event};
shared_game_data * my_data = (shared_game_data *)parameter;
for ( ; ; )
{
fprintf(stderr, "up_arrow_event initialized\n");
return_value = WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch ( return_value )
{
case WAIT_OBJECT_0 :
EnterCriticalSection(&critical_section);
if ( (my_data->dstrect.y - 1) > -1 )
{
SDL_FillRect(my_data->screen, NULL, SDL_MapRGB(my_data->screen->format, 0, 0, 0));
--my_data->dstrect.y;
SDL_FillRect(my_data->screen, 0, SDL_MapRGB(my_data->screen->format, 0, 0, 0));
SDL_BlitSurface(my_data->bmp, 0, my_data->screen, &my_data->dstrect);
SDL_Flip(my_data->screen);
}
LeaveCriticalSection(&critical_section);
ResetEvent(objects[0]);
break;
case WAIT_OBJECT_0 + 1 :
return 0;
}
}
return 0;
}
unsigned __stdcall clear_screen_handler( void * parameter )
{
DWORD return_value = 0;
HANDLE objects[2] = {clear_screen_event, escape_event};
shared_game_data * my_data = (shared_game_data *)parameter;
for ( ; ; )
{
fprintf(stderr, "clear_screen_event initialized\n");
return_value = WaitForMultipleObjects(2, objects, FALSE, INFINITE);
switch ( return_value )
{
case WAIT_OBJECT_0 :
EnterCriticalSection(&critical_section);
SDL_FillRect(my_data->screen, NULL, SDL_MapRGB(my_data->screen->format, 0, 0, 0));
my_data->dstrect.x = 0;
my_data->dstrect.y = 0;
SDL_FillRect(my_data->screen, 0, SDL_MapRGB(my_data->screen->format, 0, 0, 0));
SDL_BlitSurface(my_data->bmp, 0, my_data->screen, &my_data->dstrect);
SDL_Flip(my_data->screen);
LeaveCriticalSection(&critical_section);
ResetEvent(objects[0]);
break;
case WAIT_OBJECT_0 + 1 :
return 0;
}
}
return 0;
}
void init_events ( )
{
/* Create our keyboard events */
up_arrow_event = CreateEvent( NULL, TRUE, FALSE, TEXT("UpArrowEvent") );
if (!up_arrow_event)
ErrorExit(TEXT("CreateEvent 1"));
down_arrow_event = CreateEvent( NULL, TRUE, FALSE, TEXT("DownArrowEvent") );
if (!down_arrow_event)
ErrorExit(TEXT("CreateEvent 2"));
right_arrow_event = CreateEvent( NULL, TRUE, FALSE, TEXT("RightArrowEvent") );
if (!right_arrow_event)
ErrorExit(TEXT("CreateEvent 3"));
left_arrow_event = CreateEvent( NULL, TRUE, FALSE, TEXT("LeftArrowEvent") );
if (!left_arrow_event)
ErrorExit(TEXT("CreateEvent 4"));
escape_event = CreateEvent( NULL, TRUE, FALSE, TEXT("EscapeEvent") );
if (!escape_event)
ErrorExit(TEXT("CreateEvent 5"));
clear_screen_event = CreateEvent( NULL, TRUE, FALSE, TEXT("ClearScrenEvent") );
if(!clear_screen_event)
ErrorExit(TEXT("CreateEvent 6"));
debounce_event = CreateEvent( NULL, TRUE, FALSE, TEXT("DebounceEvent") );
if(!debounce_event)
ErrorExit(TEXT("CreateEvent 7"));
return;
}
inline shared_game_data * create_shared_game_data_node( shared_game_data * data )
{
data = malloc(sizeof(shared_game_data));
if (!data)
{
perror("Malloc");
ExitProcess(1);
}
return data;
}
inline void create_threads( void * data, unsigned * thread_id )
{
unsigned index;
DWORD msleep = 50;
DWORD deb_msleep = 50;
void * sleep = &msleep;
void * deb_sleep = &deb_msleep;
h_threads[0] = (HANDLE)_beginthreadex(NULL, 0, &event_loop, sleep, 0, &thread_id[0] );
h_threads[1] = (HANDLE)_beginthreadex(NULL, 0, &left_arrow_handler, data, 0, &thread_id[1] );
h_threads[2] = (HANDLE)_beginthreadex(NULL, 0, &right_arrow_handler, data, 0, &thread_id[2] );
h_threads[3] = (HANDLE)_beginthreadex(NULL, 0, &up_arrow_handler, data, 0, &thread_id[3] );
h_threads[4] = (HANDLE)_beginthreadex(NULL, 0, &down_arrow_handler, data, 0, &thread_id[4] );
h_threads[5] = (HANDLE)_beginthreadex(NULL, 0, &clear_screen_handler, data, 0, &thread_id[5] );
h_threads[6] = (HANDLE)_beginthreadex(NULL, 0, &debounce_events, deb_sleep, 0, &thread_id[6] );
for ( index = 0; index < MAX_THREADS; index++ )
if (!h_threads[index])
ErrorExit("_beginthreadex");
return;
}
void cleanup( )
{
close_threads();
DeleteCriticalSection(&critical_section);
close_events();
free(data);
return;
}
int main( int argc, char * argv[] )
{
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
SDL_errorexit( "SDL_Init", "SDL" );
if (atexit(SDL_Quit))
{
perror("atexit");
exit(1);
}
if (atexit(cleanup))
{
perror("Atexit");
ExitProcess(1);
}
unsigned thread_id[MAX_THREADS] = {0};
if (!InitializeCriticalSectionAndSpinCount(&critical_section, 0x0000050))
ErrorExit(TEXT("InitialCriticalSectionAndSpinCount"));
init_events();
shared_game_data * game_data = NULL;
game_data = create_shared_game_data_node(game_data);
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
atexit(SDL_Quit);
game_data->screen = SDL_SetVideoMode( 640, 480, 16, SDL_HWSURFACE | SDL_DOUBLEBUF );
if ( !game_data->screen )
SDL_errorexit("SDL_SetVideoMode", "SDL");
game_data->bmp = SDL_LoadBMP("sprite.bmp");
if (!game_data->bmp)
SDL_errorexit("SDL_LoadBMP", "SDL");
SDL_SetColorKey( game_data->bmp, SDL_SRCCOLORKEY, SDL_MapRGB(game_data->bmp->format, 255, 0, 255) );
game_data->dstrect.x = (game_data->screen->w - game_data->bmp->w) / 2;
game_data->dstrect.y = (game_data->screen->h - game_data->bmp->h) / 2;
SDL_FillRect(game_data->screen, 0, SDL_MapRGB(game_data->screen->format, 0, 0, 0));
SDL_BlitSurface(game_data->bmp, 0, game_data->screen, &game_data->dstrect);
SDL_Flip(game_data->screen);
data = game_data;
create_threads( data, thread_id );
fprintf(stderr, "create_threads returned to main\n");
WaitForMultipleObjects(MAX_THREADS, h_threads, TRUE, INFINITE);
SDL_FreeSurface(game_data->bmp);
return 0;
}